﻿using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace PageAdmin.Utils
{
    /// <summary>
    /// 解压缩帮助类，采用框架自带的System.IO.Compression
    /// </summary>
    public class ZipHelper
    {


        /// <summary>
        /// 生成压缩文件
        /// </summary>
        /// <param name="compressPath"></param>
        /// <param name="zipPath"></param>
        /// <param name="comment"></param>
        /// <param name="password"></param>
        /// <param name="compressionLevel"></param>
        /// <returns></returns>
        public static bool Compress(string compressPath, string zipPath, string comment = null, string password = null, int compressionLevel = 6)
        {
            IEnumerable<string> sourceList = new List<string>() { compressPath };
            return Compress(sourceList, zipPath, comment, password, compressionLevel);
        }

        /// <summary>
        /// 压缩多个文件/文件夹
        /// </summary>
        /// <param name="sourceList">源文件/文件夹路径列表</param>
        /// <param name="zipFilePath">压缩文件路径</param>
        /// <param name="comment">注释信息</param>
        /// <param name="password">压缩密码</param>
        /// <param name="compressionLevel">压缩等级，范围从0到9，可选，默认为6</param>
        /// <returns></returns>
        public static bool Compress(IEnumerable<string> sourceList, string zipFilePath, string comment = null, string password = null, int compressionLevel = 6)
        {
            bool result = false;
            if (sourceList.Count() == 0)
            {
                return false;
            }
            try
            {
                zipFilePath = IOHelper.MapPath(zipFilePath);
                //检测目标文件所属的文件夹是否存在，如果不存在则建立
                string zipFileDirectory = Path.GetDirectoryName(zipFilePath);
                if (!Directory.Exists(zipFileDirectory))
                {
                    Directory.CreateDirectory(zipFileDirectory);
                }
                using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFilePath)))
                {
                    zipStream.Password = password;//设置密码
                    zipStream.SetComment(comment);//添加注释
                    zipStream.SetLevel(compressionLevel);//设置压缩等级

                    foreach (string key in sourceList)//从字典取文件添加到压缩文件
                    {
                        string path = IOHelper.MapPath(key);
                        if (File.Exists(path))//判断是文件还是文件夹
                        {
                            FileInfo fileItem = new FileInfo(path);
                            using (FileStream readStream = fileItem.Open(FileMode.Open,
                                FileAccess.Read, FileShare.Read))
                            {
                                ZipEntry zipEntry = new ZipEntry(path.Replace(zipFileDirectory, ""));
                                zipEntry.DateTime = fileItem.LastWriteTime;
                                zipEntry.Size = readStream.Length;
                                zipStream.PutNextEntry(zipEntry);
                                int readLength = 0;
                                byte[] buffer = new byte[readStream.Length];
                                int bufferSize = buffer.Count();
                                do
                                {
                                    readLength = readStream.Read(buffer, 0, bufferSize);
                                    zipStream.Write(buffer, 0, readLength);
                                } while (readLength == bufferSize);

                                readStream.Close();
                            }
                        }
                        else if (Directory.Exists(path))
                        {
                            ZipEntry zipEntry = new ZipEntry(path.Replace(zipFileDirectory, "") + "/");
                            zipStream.PutNextEntry(zipEntry);
                            CompressSetp(path, zipStream, zipFileDirectory);
                        }
                    }

                    zipStream.Flush();
                    zipStream.Finish();
                    zipStream.Close();
                }

                result = true;
            }
            catch (System.Exception ex)
            {
                throw new Exception("压缩文件失败", ex);
            }

            return result;
        }



        /// <summary>
        /// 递归压缩目录
        /// </summary>
        /// <param name="strDirectory">文件夹名称</param>
        /// <param name="s">The ZipOutputStream Object.</param>
        /// <param name="parentPath"></param>

        private static void CompressSetp(string strDirectory, ZipOutputStream s, string parentPath)
        {
            if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
            {
                strDirectory += Path.DirectorySeparatorChar;
            }
            Crc32 crc = new Crc32();

            string[] filenames = Directory.GetFileSystemEntries(strDirectory);

            foreach (string file in filenames)// 遍历所有的文件和目录
            {

                if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
                {
                    //string pPath = parentPath;
                    //pPath += file.Substring(file.LastIndexOf("\\") + 1);
                    //pPath += "\\";
                    ZipEntry zipEntry = new ZipEntry(file.Replace(parentPath, "") + "/");
                    s.PutNextEntry(zipEntry);
                    CompressSetp(file, s, parentPath);
                }

                else // 否则直接压缩文件
                {
                    //打开压缩文件
                    using (FileStream fs = File.OpenRead(file))
                    {

                        byte[] buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);
                        //string fileName = parentPath + file.Substring(file.LastIndexOf("\\") + 1);
                        ZipEntry entry = new ZipEntry(file.Replace(parentPath, ""));

                        entry.DateTime = DateTime.Now;
                        entry.Size = fs.Length;

                        fs.Close();

                        crc.Reset();
                        crc.Update(buffer);

                        entry.Crc = crc.Value;
                        s.PutNextEntry(entry);

                        s.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }


        /// <summary>
        /// 解压文件
        /// </summary>
        /// <param name="zipFilePath">压缩文件名</param> /// <param name="toDirectory">解压目录目录</param>
        /// <param name="password">解压文件密码，无密码这不需要设置此参数</param>
        /// <returns></returns>
        public static string DeCompress(string zipFilePath, string toDirectory, bool isOverride = true)
        {
            zipFilePath = IOHelper.MapPath(zipFilePath);
            if (!File.Exists(zipFilePath))
            {
                throw new FileNotFoundException("要解压的文件不存在", zipFilePath);
            }
            //解压文件夹为空时默认与压缩文件同一级目录下，跟压缩文件同名的文件夹   
            if (string.IsNullOrWhiteSpace(toDirectory))
            {
                toDirectory = Path.GetDirectoryName(zipFilePath);
                string name = Path.GetFileNameWithoutExtension(zipFilePath);
                toDirectory = Path.Combine(toDirectory, name);
            }
            string unZipDir2 = toDirectory;
            char lastChar = toDirectory[toDirectory.Length - 1];
            if (lastChar != '/' && lastChar != '\\')
            {
                toDirectory += Path.DirectorySeparatorChar;
            }
            toDirectory=toDirectory.Replace("\\", "/");
            if (!Directory.Exists(toDirectory))
            {
                IOHelper.CreateDirectory(toDirectory);
            }

            //解压递归
            DeCompressStep(zipFilePath, toDirectory, isOverride);

            return unZipDir2;
        }

        /// <summary>
        /// 递归解压
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="toDirectory"></param>
        /// <param name="isOverride">是否覆盖文件</param>
        private static void DeCompressStep(string zipFilePath, string toDirectory, bool isOverride)
        {
            toDirectory = IOHelper.MapPath(toDirectory);
            ZipInputStream zipInput = null;
            FileStream fileStream = null;
            try
            {
                fileStream = File.OpenRead(zipFilePath);
                zipInput = new ZipInputStream(fileStream);
                //zipInput.Password =null;
                ZipEntry theEntry;
                while ((theEntry = zipInput.GetNextEntry()) != null)
                {
                    string entryName = theEntry.Name;
                    //string replaceName = entryName.Replace(Path.GetFileNameWithoutExtension(zipFilePath), "").Replace("/","");
                    string tempPath = Path.Combine(toDirectory, entryName);
                    if (theEntry.IsDirectory)
                    {
                        if (!Directory.Exists(tempPath))
                        {
                            Directory.CreateDirectory(tempPath);
                        }
                    }
                    else
                    {
                        if (!isOverride)
                        {
                            if (IOHelper.FileIsExists(tempPath))
                            {
                                continue;
                            }
                        }
                        using (FileStream streamWriter = File.Create(tempPath))
                        {
                            byte[] buffer = new byte[10240];
                            int size = zipInput.Read(buffer, 0, buffer.Length);
                            while (size > 0)
                            {
                                streamWriter.Write(buffer, 0, size);
                                size = zipInput.Read(buffer, 0, buffer.Length);
                            }
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                }
                if (zipInput != null)
                {
                    zipInput.Close();
                    zipInput.Dispose();
                }
            }
        }



    }
}
